#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <vector>
#include <string>
#include<iostream>
#include<fstream>
#include <windows.h>
#include <math.h>
#include "temp_matrix.hpp"

using namespace cv;
using namespace std;
TempMatrixReader::TempMatrixReader()
{
	TagNames.push_back("Planck R1");
	TagNames.push_back("Planck R2");
	TagNames.push_back("Planck O");
	TagNames.push_back("Planck F");
	TagNames.push_back("Planck B");
	TagNames.push_back("Emissivity");
    TagNames.push_back("Reflected Apparent Temperature");
	TagNames.push_back("Raw Thermal Image Type");

}

void TempMatrixReader::runExifTool(const string& exitToolPath, const string& jpgFileName, const string& outputFileName, int processModel)
{
    string command;

    // exiftool -w! %0fout.txt -PlanckR1 -PlanckR2, -PlanckO, -PlanckF, -PlanckB, -Emissivity, -ReflectedApparentTemperature -RawThermalImageType
	if (processModel == TempMatrixReader::EXIFTOOL_GETTAGS)
	{
        command = exitToolPath + " -w! %0f"+outputFileName +
            " -PlanckR1 -PlanckR2 -PlanckO -PlanckF -PlanckB -Emissivity -ReflectedApparentTemperature -RawThermalImageType "
            + jpgFileName;
        system(command.c_str());
	}
    // exiftool -w! %0fout.png -RawThermalImage -b 00001.jpg
    else if (processModel == TempMatrixReader::EXIFTOOL_GETRAWDATA)
    {
        command = exitToolPath + " -w! %0f" + outputFileName + 
            " -RawThermalImage -b " + jpgFileName;
        system(command.c_str());
    }
}

bool TempMatrixReader::parseTags(const string& txtFileName, const vector<string>& TagNames, vector<bool>& isFind, TempMatrixReader::FLIRParams& params)
{

	isFind.resize(TagNames.size());
	for (int i = 0; i < (int)TagNames.size(); ++i)
	{
		isFind[i] = false;
	}

	ifstream readFile;
	readFile.open(txtFileName, ios::in);
	string line;
	string::size_type position;
	vector<string::size_type> positions(TagNames.size());
	isFind.resize(TagNames.size());

	int i = 0;
	// read PlanckR1
	readFile.seekg(0, std::ios::beg);
	while(getline(readFile, line))
	{
		position = line.find(TagNames[i]);
		if (position != line.npos)
		{
			isFind[i] = true;
			sscanf(line.c_str(), "%*[^:]: %f", &params.PlanckR1);
		}
	}
	i++;
	// read PlanckR2
	readFile.clear();
	readFile.seekg(std::ios::beg);
	while(getline(readFile, line))
	{
		position = line.find(TagNames[i]);
		if (position != line.npos)
		{
			isFind[i] = true;
			sscanf(line.c_str(), "%*[^:]: %f", &params.PlanckR2);
		}
	}
	i++;
	// read PlanckO
	readFile.clear();
	readFile.seekg(0, std::ios::beg);
	while(getline(readFile, line))
	{
		position = line.find(TagNames[i]);
		if (position != line.npos)
		{
			isFind[i] = true;
			sscanf(line.c_str(), "%*[^:]: %f", &params.PlanckO);
		}
	}
	i++;
	// read PlanckF
	readFile.clear();
	readFile.seekg(0, std::ios::beg);
	while(getline(readFile, line))
	{
		position = line.find(TagNames[i]);
		if (position != line.npos)
		{
			isFind[i] = true;
			sscanf(line.c_str(), "%*[^:]: %f", &params.PlanckF);
		}
	}
	i++;
	// read PlanckB
	readFile.clear();
	readFile.seekg(0, std::ios::beg);
	while(getline(readFile, line))
	{
		position = line.find(TagNames[i]);
		if (position != line.npos)
		{
			isFind[i] = true;
			sscanf(line.c_str(), "%*[^:]: %f", &params.PlanckB);
		}
	}
	i++;
	// read Emissivity
	readFile.clear();
	readFile.seekg(0, std::ios::beg);
	while(getline(readFile, line))
	{
		position = line.find(TagNames[i]);
		if (position != line.npos)
		{
			isFind[i] = true;
			sscanf(line.c_str(), "%*[^:]: %f", &params.Emissivity);
		}
	}
	i++;

    // read ReflectedApparentTemperature
    readFile.clear();
    readFile.seekg(0, std::ios::beg);
    while (getline(readFile, line))
    {
        position = line.find(TagNames[i]);
        if (position != line.npos)
        {
            isFind[i] = true;
            sscanf(line.c_str(), "%*[^:]: %f", &params.ReflectedApparentTemperature);
        }
    }
    i++;
	// read RawThermalImageType
	readFile.clear();
	readFile.seekg(0, std::ios::beg);
	while(getline(readFile, line))
	{
		position = line.find(TagNames[i]);
		if (position != line.npos)
		{
			isFind[i] = true;
			char tmp[100];
			sscanf(line.c_str(), "%*[^:]: %s", tmp);
			params.RawThermalImageType = tmp;
		}
	}

	bool parseSucced = true;
	for (int i = 0; i < (int)isFind.size(); ++i)
	{
		if (isFind[i] == false)
		{
			parseSucced = false;
			break;
		}
	}
	return parseSucced;
}

void TempMatrixReader::cvt2LittleEndian(Mat& data)
{
	CV_Assert(data.type() == CV_16U);
	
	Mat temp = data.clone();

	//Mat temp(input.rows, input.cols, CV_16U);
	for (int r = 0; r < temp.rows; r++)
	{
		for (int c = 0; c < temp.cols; c++)
		{
			temp.at<unsigned short>(r, c) = data.at<unsigned short>(r, c)%256;
		}
	}
	Mat output = temp*256 + data/256;
    if (output.type()!=data.type())
    {
        output.convertTo(output, data.type());
    }
	output.copyTo(data);
}

bool TempMatrixReader::getTempMatrixSingle(const string& jpgFileName, const string& exitToolPath, Mat& TempMatrix)
{
	// step 1 use exiftool to get Tag txt file
	this->runExifTool(exitToolPath, jpgFileName, "tags.txt", EXIFTOOL_GETTAGS);

	// step 2 parse tags and parameters
	vector<bool> isTagsFind;
	FLIRParams params;
	if(!this->parseTags("tags.txt", this->TagNames, isTagsFind, params))
		return false;

	// step 3 use exiftool to get tiff or png data
	Mat rawData;
	if (params.RawThermalImageType == "PNG")
	{
		this->runExifTool(exitToolPath, jpgFileName, "raw.png", EXIFTOOL_GETRAWDATA);
		rawData = imread("raw.png", IMREAD_ANYDEPTH);
		this->cvt2LittleEndian(rawData);
	}
	else if (params.RawThermalImageType == "TIFF")
	{
		this->runExifTool(exitToolPath, jpgFileName, "raw.tiff", EXIFTOOL_GETRAWDATA);
		rawData = imread("raw.tiff", IMREAD_ANYDEPTH);
	}
	
	// step 3 convert raw data to temperature matrix
	this->computeTempMatrix(rawData, params, TempMatrix);
    return true;
}

void TempMatrixReader::computeTempMatrix(const Mat& rawData, const FLIRParams& params, Mat& TempMatrix)
{
    CV_Assert(!rawData.empty());
    CV_Assert(params.Emissivity <= 1.0f && params.Emissivity > 0.0f);
    Mat _raw = rawData.clone();
    _raw.convertTo(_raw, CV_32F);

    if (params.Emissivity == 1.0f)
    {
        Mat log1, temp;
        temp = params.PlanckR1 / (params.PlanckR2*(_raw + params.PlanckO)) + params.PlanckF;
        cv::log(temp, log1);
        TempMatrix = params.PlanckB / (log1 + 1e-6) - 273.15f;
    }
    else if (params.Emissivity < 1.0f)
    {
        float RAW_refl = params.PlanckR1 / (params.PlanckR2 * (std::exp(params.PlanckB/(params.ReflectedApparentTemperature + 273.15f))
            - params.PlanckF)) - params.PlanckO;
        Mat RAW_obj = (_raw - (1 - params.Emissivity)*RAW_refl) / params.Emissivity;
        Mat log1, temp;
        temp = params.PlanckR1 / (params.PlanckR2*(RAW_obj + params.PlanckO)) + params.PlanckF;
        cv::log(temp, log1);
        TempMatrix = params.PlanckB / (log1) - 273.15f;
    }
}